﻿// Reincarnation.cpp: определяет точку входа для приложения.
//

#include "stdafx.h"
#include "Reincarnation.h"

#include "Graphics.h"
#include "FieldCell.h"
#include "Field.h"

#pragma comment(lib,"d3d9.lib")
#pragma comment(lib,"d3dx9.lib")
#pragma comment(lib,"dinput8.lib")
#pragma comment(lib,"dxguid.lib")

using namespace std;

//////////////////////////////////////////////////	Глобальные переменные	//////////////////////////////////////////////////////////

//HINSTANCE hInst;								// текущий экземпляр			
LPCTSTR WndClassName = L"ReinWnd";				// имя класса главного окна
HWND hwnd = NULL;
HRESULT hr;										//Возможно пригодится

const int g_ciWidth  = 800;
const int g_ciHeight = 600;
bool g_bIsWindowed = 0;
int g_iWndWidth;
int g_iWndHeight;
Graphics& gr = Graphics::get();					//Тут наш граф движок
CShaderPtr g_pPixelShader, g_pVertexShader;		//Это шейдеры
MSG msg;										//Виндовс мессадж
IDirectInputDevice8* DIKeyboard;				//Управление клавой
IDirectInputDevice8* DIMouse;					//Управление мышью
DIMOUSESTATE mouseCurrState;					//Нынешний статус мыши
DIMOUSESTATE mouseLastState;					//Последний статус мыши
BYTE keyboardState[256];						//Статус клавиатуры
LPDIRECTINPUT8 DirectInput;						//Сам директ инпут
//vector<CFieldCell> vecFieldCell;
//CFieldCell::TypeOfCell CellID;
int CamMoveX=0, CamMoveY=0, CamPosX=0, CamPosY=0;
CField GameField; 

//////////////////////////////////////////////////	Предобъявления функций	//////////////////////////////////////////////////////////

LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);

bool InitWindow(HINSTANCE hInstance, int ShowWnd);			//Создание окна							done 100
bool InitD3dApp();											//Создание директ девайса				done 90
bool InitDirectInput(HINSTANCE hInstance);					//Директ инпут девайс					done 100
bool ReleaseObjects();										//Освобождение девайсов и интерфейсов	done 70
int MainMessageLoop();										//Цикл основного сообщения виндовс		done
void DetectInput();											//Временная функция определения нажатия клавиши
void MainRender();


//////////////////////////////////////////////////	Структуры данных	//////////////////////////////////////////////////////////////

typedef enum _TypeOfLO
	{
		TypePlayer		= 0,
		TypeSkeleton	= 1,
		TypeZombi		= 2,
	} TypeOfLO;



string GetFileAsString(const string& strFileName)
{
	string content;
	std::ifstream in(strFileName.c_str(), std::ios::binary);
	std::istreambuf_iterator<char> begin(in), end;
	while(begin != end)
		content += *begin++;
	return content;
}

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
	if(!InitWindow(hInstance, nCmdShow))
	{
		MessageBox(0, L"Window Initialization - Failed", L"Error", MB_OK);
		return 0;
	}

	if(!InitD3dApp())
	{
		MessageBox(0, L"Direct3D Initialization - Failed", L"Error", MB_OK);
		return 0;
	}

	if(!InitDirectInput(hInstance))
	{
		MessageBox(0, L"Direct Input Initialization - Failed", L"Error", MB_OK);
		return 0;
	}

	MainMessageLoop();	// Цикл основного сообщения

	if(!ReleaseObjects())
	{
		MessageBox(0, L"Object Release - Failed", L"Error", MB_OK);
		return 0;
	}

	return 0;
}

bool InitWindow(HINSTANCE hInstance, int ShowWnd)
{
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX);

	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= LoadIcon(hInstance, MAKEINTRESOURCE(IDI_REINCARNATION));
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= NULL;//MAKEINTRESOURCE(IDC_REINCARNATION);
	wcex.lpszClassName	= WndClassName;
	wcex.hIconSm		= LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

	if(!RegisterClassEx(&wcex))
	{
		MessageBox(NULL, L"Error registering class", L"Error", MB_OK | MB_ICONERROR);
		return 1;
	}
	
	// Выполнить инициализацию приложения:
	hwnd = CreateWindowEx(NULL, WndClassName, L"Reincarnation", WS_OVERLAPPEDWINDOW, 0, 0, g_ciWidth, g_ciHeight, NULL, NULL, hInstance, NULL);

	if(!hwnd)
	{
		MessageBox(NULL, L"Error creating window", L"Error", MB_OK | MB_ICONERROR);
		return 1;
	}

	ShowWindow(hwnd, ShowWnd);
	UpdateWindow(hwnd);

	return 1;
}

//
//  ФУНКЦИЯ: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  НАЗНАЧЕНИЕ:  обрабатывает сообщения в главном окне.
//
//  WM_COMMAND	- обработка меню приложения
//  WM_PAINT	-Закрасить главное окно
//  WM_DESTROY	 - ввести сообщение о выходе и вернуться.
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

bool ReleaseObjects()
{
	DIKeyboard->Unacquire();
	DIMouse->Unacquire();
	if(DirectInput) DirectInput->Release();

	return true;
}

bool InitD3dApp()
{
	if(!g_bIsWindowed)
	{
		g_iWndWidth = GetSystemMetrics(0);
		g_iWndHeight = GetSystemMetrics(1);
	} else
	{
		g_iWndWidth = g_ciWidth;
		g_iWndHeight = g_ciHeight;
	}

	if(!Graphics::get().Init(hwnd, g_iWndWidth, g_iWndHeight, g_bIsWindowed))				//Инициализация движка
		return FALSE;

	g_pVertexShader = Graphics::get().LoadShader("vs.hlsl", "main", "vs_2_0");				//Грузим наш вертексный шейдер

	g_pPixelShader = Graphics::get().LoadShader("ps.hlsl", "pixel_shader_main", "ps_2_0");	//Грузим наш пиксельный шейдер

	return TRUE;
}

bool InitDirectInput(HINSTANCE hInstance)
{
	DirectInput8Create(hInstance, DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&DirectInput, NULL);	//Создание девйса директ инпута

	DirectInput->CreateDevice(GUID_SysKeyboard, &DIKeyboard, NULL);										//Создание девайса клавы

	DirectInput->CreateDevice(GUID_SysMouse, &DIMouse, NULL);											//Создание девайса мыши

	DIKeyboard->SetDataFormat(&c_dfDIKeyboard);
	DIKeyboard->SetCooperativeLevel(hwnd, DISCL_FOREGROUND|DISCL_NONEXCLUSIVE);							//Реагируем на нажатия только при активном окне, не жадничаем

	DIMouse->SetDataFormat(&c_dfDIMouse);
	DIMouse->SetCooperativeLevel(hwnd, DISCL_NONEXCLUSIVE|DISCL_NOWINKEY|DISCL_FOREGROUND);				//Не жадничаем, без системных кнопок, реагируем при активном окне

	return true;
}

int MainMessageLoop()
{
	ZeroMemory(&msg, sizeof(MSG));

	/*for(int i=0; i<41; i++)
	{
		int x=i, y=0;
		if(i>=21)
		{
			y = 1;
			x = i-21;
		}
		vecFieldCell.push_back(CFieldCell(x, y, CFieldCell::TypeField));
	}

	for(int i=0; i<41; i++)
	{
		int x=i, y=2;
		if(i>=21)
		{
			y = 3;
			x = i-21;
			vecFieldCell.push_back(CFieldCell(x, y, CFieldCell::TypeForest));
		}
		else
			vecFieldCell.push_back(CFieldCell(x, y, CFieldCell::TypeForest));
	}*/

	GameField.LoadTestField();
	int X=0, Y=0;
	GameField.GetFieldSize(&X, &Y);
	D3DXVECTOR2 tempPos;
	tempPos = GameField.GetPosFromID(X, Y);
	CamPosX = -tempPos.x/2+64;
	CamPosY = -tempPos.y/2+48;
	
	while(true)
	{
		if(PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
		{
			if(msg.message == WM_QUIT)
				break;
			
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		else
		{
			DetectInput();																//Вызов функции обработки ввода

			if(!FAILED(gr.StartRender(0x00000000)))										//Начинаем отрисовку, и если не эпик фэйл, то...
			{
				MainRender();
				//for_each(vecFieldCell.begin(), vecFieldCell.end(), mem_fun_ref(&CFieldCell::Draw));
				GameField.DrawField();
				gr.DrawTxt(L"Нажмите ESC для выхода", 10, 10, 0.2f, 0.3f, 0.9f);
				gr.DrawTxt(L"Формат экрана:", 10, 30, 1.0f, 0.0f, 0.0f);
				wchar_t str[10] = L"";
				_itow(g_iWndWidth, str, 10);
				gr.DrawTxt(str, 165, 30, 1.0f, 0.0f, 0.0f);
				gr.DrawTxt(L"x", 215, 30, 1.0f, 0.0f, 0.0f);
				_itow(g_iWndHeight, str, 10);
				gr.DrawTxt(str, 230, 30, 1.0f, 0.0f, 0.0f);
				gr.DrawTxt(L"Положение камеры:", 10, 50, 1.0f, 0.0f, 0.0f);
				_itow(CamPosX, str, 10);
				gr.DrawTxt(str, 210, 50, 1.0f, 0.0f, 0.0f);
				gr.DrawTxt(L"x", 260, 50, 1.0f, 0.0f, 0.0f);
				_itow(CamPosY, str, 10);
				gr.DrawTxt(str, 275, 50, 1.0f, 0.0f, 0.0f);
				gr.DrawTxt(L"X", 960, 540, 1.0f, 0.0f, 0.0f);


				gr.EndRender();															//Заканчиваем отрисовку сцены
			}
			else
				gr.Reset();																//В случае фэйла, перезапускаем движок
		}
	}

	return (int) msg.wParam;
}

void DetectInput()
{
	DIKeyboard->Acquire();														//Захватываем клаву		эти функции мы вызывыаем каждый раз по той причине,
	DIMouse->Acquire();															//Захватываем мышь		что если переключиться на другое окно, мы потеряем девайсы

	DIMouse->GetDeviceState(sizeof(DIMOUSESTATE), &mouseCurrState);				//Получаем статус мыши

	DIKeyboard->GetDeviceState(sizeof(keyboardState), (LPVOID)&keyboardState);	//Получаем статус клавы

	if(keyboardState[DIK_ESCAPE] & 0x80)										//В случае нажатия эскейпа,
	{
		PostMessage(hwnd, WM_DESTROY, 0, 0);									//закрываем программу
	}

	POINT mousePos;
	GetCursorPos(&mousePos);

	if((mouseCurrState.lX != mouseLastState.lX) || (mouseCurrState.lY != mouseLastState.lY))
	{	//В принципе, именно под этим ифом надо производить манипуляции с мышью, но на данный момен это просто обертка для следующего ифа (1)

		/*if(mousePos.x < 1 || mousePos.x > g_iWndWidth-1)
			CamMoveX = -mouseCurrState.lX;
		else CamMoveX = 0;*/

		/*if(mousePos.y < 1 || mousePos.y > g_iWndHeight-1)
			CamMoveY = -mouseCurrState.lY;
		else CamMoveY = 0;*/

		mouseLastState = mouseCurrState;

		if(!g_bIsWindowed)
		{	//(1) Для этого. Тут, в зависимости от того окно или фуллскрин, обрабатывается мышь первое фуллскрин, а второе (2)
			if((mousePos.x < 1 && CamPosX <= -g_iWndWidth/4) || (mousePos.x > g_iWndWidth-2 && CamPosX >= -D3DXVECTOR2(GameField.GetPosFromID(9, 1)).x+g_iWndWidth/4))
			{	//Работаем с иксом(горизонтальная)
				if(mousePos.x < 1)
					CamMoveX = 20;
				else CamMoveX = -20;
			}
			else CamMoveX = 0;

			if(CamPosX >= -g_iWndWidth/4)
				CamPosX = -g_iWndWidth/4;

			if((mousePos.y < 1 && CamPosY <= -g_iWndHeight/4) || mousePos.y > g_iWndHeight-2)
			{	//Игрек(вертикальная)
				if(mousePos.y < 1)
					CamMoveY = 20;
				else CamMoveY = -20;
			}
			else CamMoveY = 0;
		}
		else
		{	//(2) окно
			RECT clientRect;
			GetClientRect(hwnd, &clientRect);
			ScreenToClient(hwnd, &mousePos);
			if((mousePos.x < clientRect.left && CamPosX <= -g_iWndWidth/4) || (mousePos.x > clientRect.right  && CamPosX >= -D3DXVECTOR2(GameField.GetPosFromID(9, 1)).x+g_iWndWidth/4))
			{
				if(mousePos.x < clientRect.left)
					CamMoveX = 20;
				else CamMoveX = -20;
			}
			else CamMoveX = 0;

			if(CamPosX >= -g_iWndWidth/4)
				CamPosX = -g_iWndWidth/4;

			if(mousePos.y < clientRect.top || mousePos.y > clientRect.bottom)
			{
				if(mousePos.y < clientRect.top)
					CamMoveY = 20;
				else CamMoveY = -20;
			}
			else CamMoveY = 0;
		}
	}
}

void MainRender()
{
	gr.SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
	gr.SetRenderState(D3DRS_LIGHTING, FALSE);
	gr.SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
	gr.SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
	gr.SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);
	gr.SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
	gr.SetRenderState(D3DRS_ZWRITEENABLE, TRUE);

	D3DXMATRIX mat, matViewProj, matProj, matView;
	D3DXMatrixIdentity(&mat);

	CamPosX += CamMoveX;
	CamPosY += CamMoveY;

	D3DXMatrixTranslation(&matView, CamPosX, CamPosY, 0);
	D3DXMatrixOrthoLH(&matProj, g_iWndWidth, -g_iWndHeight, 0, 10);

	D3DXMatrixMultiply(&matViewProj, &matView, &matProj);

	gr.SetPixelShader(g_pPixelShader);
	gr.SetVertexShader(g_pVertexShader);

	g_pVertexShader->SetMatrix("mWorld", &mat);
	g_pVertexShader->SetMatrix("mViewProjection", &matViewProj);

	gr.SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
	gr.SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
	gr.SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);

	gr.SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
	gr.SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
	gr.SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
}